package com.quectel.pay.util;

import com.alipay.api.DefaultAlipayClient;
import com.google.common.base.Preconditions;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.quectel.pay.module.merchant.dto.PayCertFileDto;
import com.quectel.pay.module.merchant.dto.PayChannelDto;
import com.quectel.pay.module.merchant.service.PayCertFileService;
import com.quectel.pay.module.merchant.service.PayChannelService;
import com.quectel.sdk.pay.wxv2.wxsdk.IWXPayDomain;
import com.quectel.sdk.pay.wxv2.wxsdk.WXPayConfig;
import com.quectel.sdk.pay.wxv2.wxsdk.WXPayConstants;
import com.quectel.util.common.JacksonUtils;
import com.quectel.util.spring.SpringUtils;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Base64;
import java.util.concurrent.TimeUnit;

/**
 * 得到支付的执行器
 *
 * @author cl
 */
public class PayInstanceUtils {
    /**
     * 支付宝客户端缓存
     */
    private static final Cache<Long, DefaultAlipayClient> Ali_CHANNEL_CACHE =
            CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build();
    /**
     * 微信JSAPI v2缓存
     */
    private static final Cache<Long, WXPayConfig> WX_JSAPI_V2_CACHE =
            CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build();
    /**
     * 微信JSAPI v3缓存
     * key channelId value RSAAutoCertificateConfig
     */
    private static final Cache<Long, RSAAutoCertificateConfig> WX_JSAPI_V3_CACHE =
            CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build();

    /**
     * 微信app v2缓存
     */
    private static final Cache<Long, WXPayConfig> WX_APP_V2_CACHE =
            CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build();

    /**
     * 微信app v3缓存
     */
    private static final Cache<Long, RSAAutoCertificateConfig> WX_APP_V3_CACHE =
            CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build();


    private PayInstanceUtils() {

    }

    /**
     * 阿里的执行器
     *
     * @param channelId
     * @return
     */
    public static DefaultAlipayClient getAliInstance(Long channelId) {
        return Ali_CHANNEL_CACHE.asMap().computeIfAbsent(channelId, key -> {
            PayChannelService payChannelService = SpringUtils.getBean(PayChannelService.class);
            PayChannelDto payChannelDto = payChannelService.selectById(channelId);
            if (payChannelDto == null) {
                throw new RuntimeException("Channel not exist");
            }
            //支付宝
            PayChannelDto.AliChannelConfig configParams = JacksonUtils.parse(payChannelDto.getPayConfig(), PayChannelDto.AliChannelConfig.class);
            return new DefaultAlipayClient(
                    Boolean.TRUE.equals(configParams.isSandbox()) ? "https://openapi.alipaydev.com/gateway.do" : "https://openapi.alipay.com/gateway.do",
                    configParams.getAppId(),
                    configParams.getPrivateKey(),
                    "json",
                    "UTF-8",
                    configParams.getPublicKey(),
                    "RSA2");
        });
    }

    /**
     * 微信JSAPI的执行器
     *
     * @param channelId
     * @return
     */
    public static WXPayConfig getWxJSAPIV2Instance(Long channelId) {

        return WX_JSAPI_V2_CACHE.asMap().computeIfAbsent(channelId, key -> {
            PayChannelService payChannelService = SpringUtils.getBean(PayChannelService.class);
            PayChannelDto payChannelDto = payChannelService.selectById(channelId);
            if (payChannelDto == null) {
                throw new RuntimeException("Channel not exist");
            }
            PayChannelDto.WeChatJSAPIChannelConfig configParams = JacksonUtils.parse(payChannelDto.getPayConfig(), PayChannelDto.WeChatJSAPIChannelConfig.class);
            PayCertFileService certFileService = SpringUtils.getBean(PayCertFileService.class);
            PayCertFileDto payCertFileDto = certFileService.selectById(configParams.getCertFileId());
            Preconditions.checkNotNull(payCertFileDto);
            return new WXPayConfig() {
                @Override
                public String getAppID() {
                    return configParams.getAppId();
                }

                @Override
                public String getSecret() {
                    return configParams.getSecret();
                }

                @Override
                public String getMchID() {
                    return configParams.getMchId();
                }

                @Override
                public String getKey() {
                    return configParams.getKey();
                }

                @Override
                public InputStream getCertStream() {
                    return new ByteArrayInputStream(Base64.getDecoder().decode(payCertFileDto.getFileBase64Date()));
                }

                @Override
                public IWXPayDomain getWXPayDomain() {
                    return new IWXPayDomain() {
                        @Override
                        public void report(String domain, long elapsedTimeMillis, Exception ex) {

                        }

                        @Override
                        public DomainInfo getDomain(WXPayConfig config) {
                            return new DomainInfo(WXPayConstants.DOMAIN_API, true);
                        }
                    };
                }
            };
        });
    }

    /**
     * 微信h5的执行器
     *
     * @param channelId
     * @return
     */
    public static RSAAutoCertificateConfig getWxJSAPIV3Instance(Long channelId) {

        return WX_JSAPI_V3_CACHE.asMap().computeIfAbsent(channelId, key1 -> {

            PayChannelService payChannelService = SpringUtils.getBean(PayChannelService.class);
            PayChannelDto payChannelDto = payChannelService.selectById(channelId);
            if (payChannelDto == null) {
                throw new RuntimeException("Channel not exist");
            }
            PayChannelDto.WeChatJSAPIChannelConfig configParams = JacksonUtils.parse(payChannelDto.getPayConfig(), PayChannelDto.WeChatJSAPIChannelConfig.class);
            PayCertFileService certFileService = SpringUtils.getBean(PayCertFileService.class);
            PayCertFileDto payCertFileDto = certFileService.selectById(configParams.getCertFileId());
            Preconditions.checkNotNull(payCertFileDto);
            return new RSAAutoCertificateConfig.Builder()
                    .merchantId(configParams.getMchId())
                    .privateKey(new String(Base64.getDecoder().decode(payCertFileDto.getFileBase64Date())))
                    .merchantSerialNumber(configParams.getMerchantSerialNumber())
                    .apiV3Key(configParams.getKey())
                    .build();
        });
    }

    /**
     * 微信app执行器
     *
     * @param channelId
     * @return
     */
    public static WXPayConfig getWxAppV2Instance(Long channelId) {

        return WX_APP_V2_CACHE.asMap().computeIfAbsent(channelId, key -> {
            PayChannelService payChannelService = SpringUtils.getBean(PayChannelService.class);
            PayChannelDto payChannelDto = payChannelService.selectById(channelId);
            if (payChannelDto == null) {
                throw new RuntimeException("Channel not exist");
            }
            PayChannelDto.WeChatJSAPIChannelConfig configParams = JacksonUtils.parse(payChannelDto.getPayConfig(), PayChannelDto.WeChatJSAPIChannelConfig.class);
            PayCertFileService certFileService = SpringUtils.getBean(PayCertFileService.class);
            PayCertFileDto payCertFileDto = certFileService.selectById(configParams.getCertFileId());
            Preconditions.checkNotNull(payCertFileDto);
            return new WXPayConfig() {
                @Override
                public String getAppID() {
                    return configParams.getAppId();
                }

                @Override
                public String getSecret() {
                    return configParams.getSecret();
                }

                @Override
                public String getMchID() {
                    return configParams.getMchId();
                }

                @Override
                public String getKey() {
                    return configParams.getKey();
                }

                @Override
                public InputStream getCertStream() {
                    return new ByteArrayInputStream(Base64.getDecoder().decode(payCertFileDto.getFileBase64Date()));
                }

                @Override
                public IWXPayDomain getWXPayDomain() {
                    return new IWXPayDomain() {
                        @Override
                        public void report(String domain, long elapsedTimeMillis, Exception ex) {

                        }

                        @Override
                        public DomainInfo getDomain(WXPayConfig config) {
                            return new DomainInfo(WXPayConstants.DOMAIN_API, true);
                        }
                    };
                }
            };
        });
    }

    /**
     * 微信app执行器
     *
     * @param channelId
     * @return
     */
    public static RSAAutoCertificateConfig getWxAppV3Instance(Long channelId) {

        return WX_APP_V3_CACHE.asMap().computeIfAbsent(channelId, key1 -> {

            PayChannelService payChannelService = SpringUtils.getBean(PayChannelService.class);
            PayChannelDto payChannelDto = payChannelService.selectById(channelId);
            if (payChannelDto == null) {
                throw new RuntimeException("Channel not exist");
            }
            PayChannelDto.WeChatAppChannelConfig configParams = JacksonUtils.parse(payChannelDto.getPayConfig(), PayChannelDto.WeChatAppChannelConfig.class);
            PayCertFileService certFileService = SpringUtils.getBean(PayCertFileService.class);
            PayCertFileDto payCertFileDto = certFileService.selectById(configParams.getCertFileId());
            Preconditions.checkNotNull(payCertFileDto);

            return new RSAAutoCertificateConfig.Builder()
                    .merchantId(configParams.getMchId())
                    .privateKey(new String(Base64.getDecoder().decode(payCertFileDto.getFileBase64Date())))
                    .merchantSerialNumber(configParams.getMerchantSerialNumber())
                    .apiV3Key(configParams.getKey())
                    .build();
        });
    }

}
